#ifndef DATA_SCENE_H_
#define DATA_SCENE_H_
#include <boost/filesystem.hpp>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>

#include "circular_buffer.h"
#include "data_type.h"
#include "perception_defs.h"

///@brief max dataframe buffer size
constexpr std::uint32_t kMaxDataFrameBufferSize{30U};

using DataFrameInfo =
    std::unordered_map<std::string, std::vector<boost::filesystem::path>>;

class DataScene {
 public:
  DataScene() noexcept
      : is_init_{false}, is_dispose_{false}, is_replay_{false} {};
  ~DataScene();

  DataScene(DataScene const&) = delete;
  DataScene& operator=(DataScene const&) = delete;
  DataScene(DataScene&& rhs) noexcept {
    is_init_ = rhs.is_init_;
    is_dispose_ = rhs.is_dispose_;
    is_replay_ = rhs.is_replay_;
  };
  DataScene& operator=(DataScene&&) = delete;

  ///@brief init
  ///@param settings DataManagerSettings
  void Init(std::string const& path) noexcept;

  ///@brief buffer is empty
  ///@return bool, if empty true,else false
  bool IsEmpty() const noexcept {
    std::lock_guard<std::mutex> lock(mutex_);
    return data_frame_buffer_.IsEmpty();
  }

  void Play() noexcept {
    std::lock_guard<std::mutex> lock(mutex_);
    current_frame_index_ = 0;
  }

  bool IsFrameRunOut() const noexcept {
    std::lock_guard<std::mutex> lock(mutex_);
    return data_frame_buffer_.IsEmpty() &&
           current_frame_index_ >= frame_number_ - 1;
  }

  ///@brief retrieve head data frame
  ///@return head data frame
  DataFrame const& RetrieveDataFrame() noexcept {
    std::lock_guard<std::mutex> lock(mutex_);
    return data_frame_buffer_.Pop();
  };

  ///@brief get calibration data
  ///@return calib data array ref
  FixedArray<MatrixData, kMaxCalibDataNum> const& GetCalibData()
      const noexcept {
    return calib_data_;
  }

  ///@brief get scene data path
  ///@return data path
  std::string GetDataPath() const noexcept { return data_path_; }

  /// @brief set replay flag
  /// @param flag if replay set true,else false
  void SetRelplay(bool flag) noexcept { is_replay_ = flag; };

  /// @brief write Map to file
  /// @param path path root
  /// @param map map
  /// @param err error message
  /// @return if no error return true, otherwise return false
  bool WriteMap(std::string const& path, Map const& map,
                std::string& err) noexcept;

  /// @brief write PerceptionResult to file
  /// @param path path root
  /// @param perception perception
  /// @param err error message
  /// @return if no error return true, otherwise return false
  bool WriteObject(std::string const& path, PerceptionResult const& perception,
                   std::string& err) noexcept;

 protected:
  ///@brief load data scene
  ///@param data_scene DataScene
  static void load_data_scene(DataScene* const data_scene) noexcept;

  ///@brief travel timestamp data file
  ///@param path root path
  ///@param files time stamp path
  void travel_timestamp_data_file(
      boost::filesystem::path const& path,
      std::vector<boost::filesystem::path>& files) noexcept;

  ///@brief parse data frame info
  ///@param root_path path root
  ///@param info data frame information
  ///@param err error message
  ///@return bool, if true parse ok, false parse error
  bool parse_data_frame_info(boost::filesystem::path const& root_path,
                             DataFrameInfo& info, std::string& err) noexcept;

  ///@brief read lidar data file
  ///@param path data path
  ///@param feature feature number
  ///@param point_cloud point cloud
  ///@param err error message
  ///@return bool, if true read ok, false read error
  bool read_lidar_data(std::string const& path,
                       std::uint32_t const& feature_number,
                       PointCloud<PointXYZIT, kMaxPointNum>& point_cloud,
                       std::string& err) noexcept;

  ///@brief read radar data file
  ///@param path data path
  ///@param feature feature number
  ///@param point_cloud point cloud
  ///@param err error message
  ///@return bool, if true read ok, false read error
  bool read_radar_data(std::string const& path,
                       std::uint32_t const& feature_number,
                       PointCloud<PointXTZDopppler, kMaxPointNum>& point_cloud,
                       std::string& err) noexcept;

  ///@brief read calib data
  ///@param path data path
  ///@param data_array data
  ///@param err error message
  ///@return bool, if true read ok, false read error
  bool read_calib_data(std::string const& path,
                       FixedArray<MatrixData, kMaxCalibDataNum>& data_array,
                       std::string& err) noexcept;

  ///@brief read calib data
  ///@param path data path
  ///@param mat calibration matrix
  ///@param err error message
  ///@return bool, if true read ok, false read error
  bool parse_matrix_data(std::string const& path, Eigen::MatrixXf& mat,
                         std::string& err) noexcept;

 private:
  ///@brief mutex , multi thread safe
  mutable std::mutex mutex_;
  ///@brief init flag
  bool is_init_;
  ///@brief data path
  std::string data_path_;
  ///@brief data frame buffer
  CircularBuffer<DataFrame, kMaxDataFrameBufferSize> data_frame_buffer_;
  ///@brief calib data
  FixedArray<MatrixData, kMaxCalibDataNum> calib_data_;
  ///@brief data scene load task thread
  std::thread data_scene_load_task;
  ///@brief is dispose
  bool is_dispose_;
  /// @brief sync path name
  /// @brief sync path name
  bool is_replay_;

  /// @brief frame number
  int32_t frame_number_;

  /// @brief current frame index
  int32_t current_frame_index_;
};

#endif  // !DATA_SCENE_H_